// Copyright 2011 Dave Ruest.  All rights reserved.

/**
 * @fileoverview Honeybee is a casual nonviolent educational family game.
 * The player visits flowers gathering nectar while avoiding hazards.  
 * 
 * This file contains the game object, its helpers score, pause, collision and effect
 * as well as callbacks for the hooks in game.html.
 * 
 * @author Dave Ruest dave.ruest@gmail.com
 */

/**
 * Game constructor. It is the central point of control, creating all model elements
 * and calling their onInterval() and draw() in the event loop.  
 */
function Game() {
	this.canvas = document.getElementById("canvas");
	this.graphics = canvas.getContext("2d");
	this.width = canvas.width;
	this.height = canvas.height;
	
	this.dx = -2;
	this.dy = 0;

	// in case the image can load before we finish
	this.loading = 1;
	
	var src = "https://dave-ruest-honeybee.googlecode.com/git/dave-ruest-honeybee/startDialog.png";
	this.startDialog = this.getImage(src);
	src = "https://dave-ruest-honeybee.googlecode.com/git/dave-ruest-honeybee/honeycomb.png";
	var comb = new ImageGrid(this.graphics, this.getImage(src), 72, 3, 5);
	
	var stages = new Array();
	stages.push(new Stage(this, comb, 5));
	stages.push(new Clover(this, 15));
	stages.push(new Stage(this, comb, 6));
	this.background = new Background(this, 72, stages);
	
	src = "https://dave-ruest-honeybee.googlecode.com/git/dave-ruest-honeybee/honeybee.png";
	this.bee = new Honeybee(this, src, 72, 3, 3);
	
	src = "https://dave-ruest-honeybee.googlecode.com/git/dave-ruest-honeybee/feedback.png";
	this.feedback = new ImageGrid(this.graphics, this.getImage(src), 54, 1, 2);
	this.collision = new Collision(this);
	this.effects = new Array();
	
	src = "https://dave-ruest-honeybee.googlecode.com/git/dave-ruest-honeybee/status.png";
	this.status = new ImageGrid(this.graphics, this.getImage(src), 54, 1, 3);
	this.pause = new Pause(this);
	this.score = new Score(this);
	
	src = "https://dave-ruest-honeybee.googlecode.com/git/dave-ruest-honeybee/endDialogs.png";
	this.endDialog = new ImageGrid(this.graphics, this.getImage(src), 192, 1, 2);
	
	this.start = new Start(this);
	this.loading--;
};

/**
 * Utility method to load an image file. Updates the games "loading" count so 
 * that the event loop knows when all images are loaded and the game is ready
 * to start.
 * @param {String} source name of a file containing image data
 * @returns {Image} an image already in the process of loading
 */
Game.prototype.getImage = function(source) {
	this.loading++;
	
	var image = new Image();
	image.game = this;
	image.onload = function() {
		this.game.loading--;
	};
	image.src = source;
	return image;
};

/**
 * The real work of the event thread. Send onInterval() and draw() to the
 * right objects at the right time.
 */
Game.prototype.onInterval = function() {
	if (this.loading) {
		return;
	}
	
	if (!this.pause.paused && ! this.start) {
		this.background.onInterval();
		this.bee.onInterval();
		this.collision.onInterval();
		for (var i = 0; i < this.effects.length; i++) {
			this.effects[i].onInterval();
		}
	}
		
	this.background.draw();
	this.bee.draw();
	for (var i = 0; i < this.effects.length; i++) {
		this.effects[i].draw();
	}
	this.score.draw();
	this.pause.draw();
	if (this.start) {
		this.start.draw();
	}
};

/**
 * Collision constructor. Checks the bee location against stationary bonuses and
 * hazards. After several consecutive collisions adds creates an appropriate effect.
 * @param game
 * @returns {Collision}
 */
function Collision(game) {
	this.game = game;
};

Collision.prototype.onInterval = function() {
	var x = this.game.bee.x + this.game.background.tileSize / 2;
	var y = this.game.bee.y + this.game.background.tileSize / 2;
	var station = this.game.background.getCollision(x, y);
	
	if (this.station && this.station == station) {
		this.counter++;
		if (this.counter == 10) {
			this.game.effects.push(new Effect(this.game, this.station));
		}
	} else {
		this.station = station;
		this.counter = 0;
	}
};

/**
 * Effect constructor. After the bee has hit a flower or pesticide for
 * a short while, the score is updated and an image is faded in and
 * out.
 * @param {Game} game
 * @param {Station} station
 * @returns {Effect}
 */
function Effect(game, station) {
	this.game = game;
	this.station = station;
	this.counter = 0;
	
	this.x = game.bee.x + game.background.tileSize / 2;
	this.y = game.bee.y - this.game.feedback.size / 2;
	
	// TODO pick a random bonus... 0-2? or 1-3?
	// level generation wise might be best to define in the station
	this.r = 0;
	if (station.id == "pesticide") {
		this.c = 1;
		this.game.score.value--;
	} else if (station.id == "flower") {
		this.c = 0;
		this.game.score.value++;
	}
	
	if (this.game.score.value <= 0) {
		this.game.start = new End(this.game);
	}
};

Effect.prototype.draw = function() {
	if (this.counter <= 10) {
		this.game.graphics.globalAlpha = this.counter / 10;
	} else {
		this.game.graphics.globalAlpha = Math.abs(this.counter - 30) / 20;
	}
	this.game.feedback.draw(this.x, this.y, this.r, this.c);
	this.game.graphics.globalAlpha = 1.0;
};

Effect.prototype.onInterval = function() {
	this.x += this.game.dx;
	this.counter++;
	if (this.counter > 30) {
		this.game.effects.shift();
	}
};

// declare the singleton game instance
var game;

// callback for body.onLoad
// instantiate the singleton game instance
// start the timer
// starting loading resources
var doLoad = function() {
	game = new Game();
	document.onkeydown=doKeyDown;
	document.onkeyup=doKeyUp;
	// interval is in milliseconds, so...
	// 1k ms in 1 second, and we want 25 updates per second
	// thus 40 ms to complete each loop
	setInterval(onInterval, 1000/25);
};

// handler for window.setInterval()
var onInterval = function() {
	game.onInterval();
};

// callback for canvas.onmousedown
var doMouseDown = function(event) {
	// don't modify dx while paused or bee will change direction
	if (game.pause.paused) {
		return;
	}
	
	var e = event;
	if (!e) e = window.event;
	
	var y = e.y;
	if (e.touches) {
		y = e.touches[0].pageY - game.canvas.offsetTop;
	}
	
	if (y < game.height / 2) {
		game.dy = -2;
	} else {
		game.dy = 2;
	}
//	game.score.value = e.y + " " + e.pageY + " " + game.canvas.offsetTop;
};

// callback for canvas.onmouseup
var doMouseUp = function(event) {
	game.dy = 0;
	
	var e = event;
	if (!e) e = window.event;
	
	var x, y;
	if (e.touches) {
		x = e.x;
		y = e.y;
	} else {
		x = e.pageX - game.canvas.offsetLeft;
		y = e.pageY - game.canvas.offsetTop;
	}
	game.pause.onClick(x, y);
};

// callback for body.onKeyDown
var doKeyDown = function(e) {
	if (e.shiftKey || e.controlKey || e.metaKey || e.altKey) {
		return true;
	}
	// don't modify dx while paused or bee will change direction
	if (game.pause.paused) {
		return;
	}
	
	if (e.keyCode == 37) { // left
//		game.dx = -2;
	} else if (e.keyCode == 38 || e.keyCode == 65 || e.keyCode == 83 || e.keyCode == 68) { // up, a, s, or d
		game.dy = -2;
	} else if (e.keyCode == 39) { // right
//		game.dx = 2;
	} else if (e.keyCode == 40 || e.keyCode == 90 || e.keyCode == 88 || e.keyCode == 67) { // down, z, x, or c
		game.dy = 2;
	}
	
	return true;
};

// callback for body.onKeyUp
var doKeyUp = function(e) {
	if (e.shiftKey || e.controlKey || e.metaKey || e.altKey) {
		return true;
	}
	
	game.keyCode = e.keyCode;
	
	if (e.keyCode == 32) { // space / pause
		game.paused = ! game.paused;
	} else if (e.keyCode == 37 || e.keyCode == 39) { // left or right
//		game.dx = 0;
	} else if (e.keyCode == 38 || e.keyCode == 65 || e.keyCode == 83 || e.keyCode == 68
			|| e.keyCode == 40 || e.keyCode == 90 || e.keyCode == 88 || e.keyCode == 67) { // up or down
		game.dy = 0;
	}
	
	return true;
};